/************************************************
 * Author       :   XXY
 * Blog         :   https://www.cnblogs.com/jzyl
 * Gitee        :   https://gitee.com/xiaobaichong
 * Email        :   1047185209@QQ.com
 * FileName     :   MessageBoxCanvas.cs
 * CreateData   :   2023/8/18 12:40:04
 * UnityVersion :   2021.3.20f1c1
 * Description  :   MessageBox 消息弹窗
************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using TMPro;

namespace Main.Framework
{
    public class MessageBoxCanvas : MonoBehaviour
    {
        public class MessageBox
        {
            private Button okBtn;
            private Button cancelBtn;
            private Button closeBtn;
            private Button sparkleBtn;
            private Text title;
            private TextMeshProUGUI message;
            private RectTransform DragBg;
            private UnityAction<MessageBox> recoveryAction;
            private ScrollRect scrollRect;
            private AudioClip audio;
            private float scaleTime;
            private float sparkleTime;
            public RectTransform target;
            public MessageBox(Transform target, string msg, string title, AudioClip audioClip, UnityAction<MessageBox> recoveryAction, float scaleTime, float sparkleTime, string okTxt, UnityAction okAction, string cancelTxt, UnityAction cancelAction = null)
            {
                Init(target);
                this.recoveryAction = recoveryAction;
                this.scaleTime = scaleTime;
                this.sparkleTime = sparkleTime;

                this.title.text = title;
                this.message.text = msg;
                this.audio = audioClip;
                scrollRect.verticalNormalizedPosition = 1;
                closeBtn.onClick.AddListener(OnClose);
                sparkleBtn.onClick.AddListener(() => sparkleBtn.StartCoroutine(SparkleAnimation()));
                if (okAction == null)
                {
                    okBtn.gameObject.SetActive(false);
                }
                else
                {
                    okBtn.gameObject.SetActive(true);
                    okBtn.transform.GetChild(0).GetComponent<Text>().text = okTxt;
                    okBtn.onClick.AddListener(okAction);
                    okBtn.onClick.AddListener(OnClose);
                }
                if (cancelAction == null)
                {
                    cancelBtn.gameObject.SetActive(false);
                }
                else
                {
                    cancelBtn.gameObject.SetActive(true);
                    cancelBtn.transform.GetChild(0).GetComponent<Text>().text = cancelTxt;
                    cancelBtn.onClick.AddListener(cancelAction);
                    cancelBtn.onClick.AddListener(OnClose);
                }
                DragBg.GetComponent<MonoBehaviour>().StartCoroutine(ScaleAnimation());
            }
            private void Init(Transform transform)
            {
                this.target = (RectTransform)transform;
                okBtn = target.Find("DragBG/BottomBar/OkBtn").GetComponent<Button>();
                cancelBtn = target.Find("DragBG/BottomBar/CancelBtn").GetComponent<Button>();
                closeBtn = target.Find("DragBG/TopBar/CloseBtn").GetComponent<Button>();
                sparkleBtn = target.GetComponent<Button>();
                message = target.Find("DragBG/ContentView/Viewport/Content/message").GetComponent<TextMeshProUGUI>();
                title = target.Find("DragBG/TopBar/Title").GetComponent<Text>();
                DragBg = target.Find("DragBG").GetComponent<RectTransform>();
                scrollRect = target.Find("DragBG/ContentView").GetComponent<ScrollRect>();
                DragBg.GetComponent<Outline>().enabled = false;

                EventTrigger trigger;
                if (!DragBg.TryGetComponent<EventTrigger>(out trigger))
                {
                    trigger = DragBg.gameObject.AddComponent<EventTrigger>();
                }
                trigger.triggers.Clear();

                EventTrigger.Entry begin = new EventTrigger.Entry();
                begin.eventID = EventTriggerType.BeginDrag;
                begin.callback.AddListener(OnBeginDrag);
                trigger.triggers.Add(begin);

                EventTrigger.Entry dragging = new EventTrigger.Entry();
                dragging.eventID = EventTriggerType.Drag;
                dragging.callback.AddListener(OnDrag);
                trigger.triggers.Add(dragging);
            }
            Vector2 offsetPos;
            private void OnBeginDrag(BaseEventData arg)
            {
                PointerEventData pointer = arg as PointerEventData;
                if (RectTransformUtility.ScreenPointToLocalPointInRectangle(target, pointer.position, null, out Vector2 pos))
                {
                    offsetPos = pos - DragBg.anchoredPosition;
                }
            }
            private void OnDrag(BaseEventData arg)
            {
                PointerEventData pointer = arg as PointerEventData;

                if (RectTransformUtility.ScreenPointToLocalPointInRectangle(target, pointer.position, null, out Vector2 pos))
                {
                    pos -= offsetPos;
                    pos.x = Mathf.Clamp(pos.x, (-target.rect.width + DragBg.rect.width) / 2, (target.rect.width - DragBg.rect.width) / 2);
                    pos.y = Mathf.Clamp(pos.y, (-target.rect.height + DragBg.rect.height) / 2, (target.rect.height - DragBg.rect.height) / 2);
                    DragBg.anchoredPosition = pos;
                }
            }

            private void OnClose()
            {
                DragBg.anchoredPosition = Vector2.zero;
                okBtn.onClick.RemoveAllListeners();
                cancelBtn.onClick.RemoveAllListeners();
                closeBtn.onClick.RemoveAllListeners();
                sparkleBtn.onClick.RemoveAllListeners();
                DragBg.GetComponent<EventTrigger>().triggers.Clear();
                recoveryAction?.Invoke(this);
            }
            private IEnumerator ScaleAnimation()
            {
                // 缩放动画
                float temp = 0;
                while (temp < scaleTime)
                {
                    temp = Mathf.Clamp(temp + Time.deltaTime, 0, scaleTime);
                    DragBg.transform.localScale = Vector3.one * (temp / scaleTime);
                    yield return null;
                }
                // 适配高度
                float textHeight = message.GetComponent<RectTransform>().rect.height;
                message.transform.parent.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, textHeight + 50);
            }
            private IEnumerator SparkleAnimation()
            {
                if (audio) AudioSource.PlayClipAtPoint(audio, Vector3.zero);
                float temp = 0;
                Outline outline = DragBg.GetComponent<Outline>();
                while (temp < sparkleTime)
                {
                    temp = Mathf.Clamp(temp + Time.deltaTime, 0, sparkleTime);
                    yield return null;
                    yield return null;
                    yield return null;
                    outline.enabled = !outline.enabled;
                }
                outline.enabled = false;
            }
        }

        [SerializeField, Range(0.1f, 1)] float scaleTime = 0.2f;        // 缩放时间
        [SerializeField, Range(0.1f, 1)] float sparkleTime = 0.4f;      // 闪烁时间
        [SerializeField] AudioClip audioClip;                                   // 警报提示
        private GameObject prefab;
        private List<MessageBox> boxes = new List<MessageBox>();    // 消息盒子
        private Queue<GameObject> pool = new Queue<GameObject>();   // 对象池
        private void Awake()
        {
            prefab = transform.GetChild(0).gameObject;
            prefab.SetActive(false);
        }

        private void Update()
        {

        }
        private GameObject GetPoolObj()
        {
            GameObject obj = null;
            if (pool.Count > 0)
            {
                obj = pool.Dequeue();
            }
            else
            {
                obj = Instantiate(prefab, transform);
            }
            obj.SetActive(true);
            obj.transform.SetAsLastSibling();
            return obj;
        }
        private void OnMessageBoxRecovery(MessageBox messageBox)
        {
            boxes.Remove(messageBox);
            GameObject go = messageBox.target.gameObject;
            go.SetActive(false);
            pool.Enqueue(go);
        }
        public void Show(string title, string msg)
        {
            Show(msg, title, "", null);
        }
        public void Show(string title, string msg, string okTxt, UnityAction okAction)
        {
            Show(msg, title, okTxt, okAction, "", null);
        }
        public void Show(string title, string msg, string okTxt, UnityAction okAction, string cancelTxt, UnityAction cancelAction = null)
        {
            MessageBox box = new MessageBox(GetPoolObj().transform, msg, title, audioClip, OnMessageBoxRecovery, scaleTime, sparkleTime, okTxt, okAction, cancelTxt, cancelAction);
            boxes.Add(box);
        }
    }
}
